﻿// Released to the public domain. Use, modify and relicense at will.

using System;
using System.Collections.Generic;
using System.Linq;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using OpenTK.Input;

namespace Lucy
{
   class Game : GameWindow
   {
      /// <summary>Creates a 800x600 window with the specified title.</summary>
      public Game()
         : base(800, 600, GraphicsMode.Default, "OpenTK Quick Start Sample")
      {
         VSync = VSyncMode.On;
      }

      /// <summary>Load resources here.</summary>
      /// <param name="e">Not used.</param>
      protected override void OnLoad(EventArgs e)
      {
         base.OnLoad(e);

         GL.ClearColor(0.1f, 0.2f, 0.5f, 0.0f);
         GL.Enable(EnableCap.DepthTest);
      }

      /// <summary>
      /// Called when your window is resized. Set your viewport here. It is also
      /// a good place to set up your projection matrix (which probably changes
      /// along when the aspect ratio of your window).
      /// </summary>
      /// <param name="e">Not used.</param>
      protected override void OnResize(EventArgs e)
      {
         base.OnResize(e);

         GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);

         Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Width / (float)Height, 1.0f, 64.0f);
         GL.MatrixMode(MatrixMode.Projection);
         GL.LoadMatrix(ref projection);
      }

      /// <summary>
      /// Called when it is time to setup the next frame. Add you game logic here.
      /// </summary>
      /// <param name="e">Contains timing information for framerate independent logic.</param>
      protected override void OnUpdateFrame(FrameEventArgs e)
      {
         base.OnUpdateFrame(e);

         if (Keyboard[Key.Escape])
            Exit();
      }

      /// <summary>
      /// Called when it is time to render the next frame. Add your rendering code here.
      /// </summary>
      /// <param name="e">Contains timing information.</param>
      protected override void OnRenderFrame(FrameEventArgs e)
      {
         base.OnRenderFrame(e);

         GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

         Matrix4 modelview = Matrix4.LookAt(new Vector3(0.0f, 0.0f, -4.0f), Vector3.UnitZ, Vector3.UnitY);
         GL.MatrixMode(MatrixMode.Modelview);
         GL.ShadeModel(ShadingModel.Smooth);
         GL.LoadMatrix(ref modelview);

         GL.Translate(0.0f, 0.0f, 4.0f);
         GL.Rotate(rotation_, 0.0f, 1.0f, 0.0f);

         Geometry.Icosahedron icos = new Geometry.Icosahedron(1.0f);
         GL.Begin(BeginMode.Triangles);

         List<Geometry.Triangle> triangles = icos.triangles.ToList();                  // 20 triangles
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 80
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 320
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 1280
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 5120
         //triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 20480
         triangles.ForEach(triangle => triangle.vertices.ToList().ForEach(vertex =>
         {
            // Project the point out to our sphere of radius 4.0
            float scale = 1.0f / (float)Math.Sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z);
            vertex = vertex.Mul(scale);

            // Vertices vary from -1.0 to +1.0, we want colours to vary from 0 to 1.0
            Geometry.Point3D colors = vertex.Add(1.0f, 1.0f, 1.0f).Mul(0.5f);
            GL.Color3(colors.x, colors.y, colors.z);

            float radius = 1.4f;
            vertex = vertex.Mul(radius);
            GL.Vertex3(vertex.x, vertex.y, vertex.z);
            //if (1000 > index_)
            //{
            //   logger_.debugFormat("{0} {1}", index_++, vertex.ToString("0.0"));
            //}
         }));
         GL.End();

         rotation_ += 1.0f;
         SwapBuffers();
      }

      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main()
      {
         // The 'using' idiom guarantees proper resource cleanup.
         // We request 30 UpdateFrame events per second, and unlimited
         // RenderFrame events (as fast as the computer can handle).
         using (Game game = new Game())
         {
            game.Run(10.0);
         }
      }

      private float rotation_ = 0.0f;
   }
}